package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.http.HttpHeaderMap;
import cc.shacocloud.mirage.restful.http.MultipartFileUpload;
import cc.shacocloud.mirage.restful.http.MultipartFileUploadImpl;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.ParsedHeaderValues;
import io.vertx.ext.web.Session;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 基于{@link io.vertx.ext.web.RoutingContext}，{@link HttpServerRequest} 的实现
 */
public class VertXHttpRequestImpl implements HttpRequest {
    
    private final io.vertx.ext.web.RoutingContext routingContext;
    private final HttpServerRequest httpServerRequest;
    private final HttpHeaderMap httpHeaderMap;
    private final RoutingContext nativeRoutingContext;
    
    public VertXHttpRequestImpl(io.vertx.ext.web.@NotNull RoutingContext routingContext,
                                RoutingContext nativeRoutingContext) {
        this.nativeRoutingContext = nativeRoutingContext;
        this.routingContext = routingContext;
        this.httpServerRequest = routingContext.request();
        this.httpHeaderMap = new HttpHeaderMap(httpServerRequest.headers());
    }
    
    @Override
    public RoutingContext context() {
        return nativeRoutingContext;
    }
    
    @Override
    public HttpResponse response() {
        return nativeRoutingContext.response();
    }
    
    @Override
    public String getScheme() {
        return httpServerRequest.scheme();
    }
    
    @Override
    public String getServerName() {
        String host = headers().get(HttpHeaders.HOST);
        if (host != null && !"".equals(host.trim())) {
            if (host.startsWith("[")) {
                host = host.substring(1, host.indexOf(93));
            } else if (host.indexOf(58) != -1) {
                host = host.substring(0, host.indexOf(58));
            }
        } else {
            SocketAddress socketAddress = httpServerRequest.localAddress();
            host = socketAddress != null ? socketAddress.host() : null;
        }
        
        return host;
    }
    
    @Override
    public int getServerPort() {
        String host = headers().get(HttpHeaders.HOST);
        if (host != null) {
            int colonIndex;
            if (host.startsWith("[")) {
                colonIndex = host.indexOf(58, host.indexOf(93));
            } else {
                colonIndex = host.indexOf(58);
            }
            
            if (colonIndex != -1) {
                try {
                    return Integer.parseInt(host.substring(colonIndex + 1));
                } catch (NumberFormatException ignored) {
                }
            }
            
            if (getScheme().equals("https")) {
                return 443;
            }
            
            if (getScheme().equals("http")) {
                return 80;
            }
        }
        SocketAddress socketAddress = httpServerRequest.localAddress();
        return socketAddress != null ? socketAddress.port() : -1;
    }
    
    @Override
    public HttpVersion version() {
        return httpServerRequest.version();
    }
    
    @Override
    public HttpMethod method() {
        return httpServerRequest.method();
    }
    
    @Override
    public HttpHeaderMap headers() {
        return httpHeaderMap;
    }
    
    @Override
    public boolean isSSL() {
        return httpServerRequest.isSSL();
    }
    
    @Override
    public String uri() {
        return httpServerRequest.uri();
    }
    
    @Override
    public String absoluteURI() {
        return httpServerRequest.absoluteURI();
    }
    
    @Override
    public String path() {
        return httpServerRequest.path();
    }
    
    @Override
    public String query() {
        return httpServerRequest.query();
    }
    
    @Override
    public String host() {
        return httpServerRequest.host();
    }
    
    @Override
    public long bytesRead() {
        return httpServerRequest.bytesRead();
    }
    
    @Override
    public SocketAddress remoteAddress() {
        return httpServerRequest.remoteAddress();
    }
    
    @Override
    public SocketAddress localAddress() {
        return httpServerRequest.localAddress();
    }
    
    @Override
    public SSLSession sslSession() {
        return httpServerRequest.sslSession();
    }
    
    @Override
    public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
        return httpServerRequest.peerCertificateChain();
    }
    
    @Override
    public boolean isEnded() {
        return httpServerRequest.isEnded();
    }
    
    @Override
    public Cookie getCookie(String name) {
        return httpServerRequest.getCookie(name);
    }
    
    @Override
    public int cookieCount() {
        return httpServerRequest.cookieCount();
    }
    
    @Override
    public Map<String, Cookie> cookieMap() {
        return httpServerRequest.cookies()
                .stream()
                .collect(Collectors.toMap(Cookie::getName, cookie -> cookie));
    }
    
    @Override
    public String getBodyAsString() {
        return routingContext.body().asString();
    }
    
    @Override
    public String getBodyAsString(String encoding) {
        return routingContext.body().asString(encoding);
    }
    
    @Override
    public JsonObject getBodyAsJson() {
        return routingContext.body().asJsonObject();
    }
    
    @Override
    public JsonArray getBodyAsJsonArray() {
        return routingContext.body().asJsonArray();
    }
    
    @Override
    public Buffer getBody() {
        return routingContext.body().buffer();
    }
    
    @Override
    public MultipartFileUpload fileUploads() {
        return new MultipartFileUploadImpl(routingContext);
    }
    
    @Override
    public Session session() {
        return routingContext.session();
    }
    
    @Override
    public boolean isSessionAccessed() {
        return routingContext.isSessionAccessed();
    }
    
    @Override
    public ParsedHeaderValues parsedHeaders() {
        return routingContext.parsedHeaders();
    }
    
    @Override
    public Map<String, String> pathParams() {
        return routingContext.pathParams();
    }
    
    @Override
    public String pathParam(String name) {
        return routingContext.pathParam(name);
    }
    
    @Override
    public MultiMap queryParams() {
        return routingContext.queryParams();
    }
    
    @Override
    public List<String> queryParams(String name) {
        return queryParams().getAll(name);
    }
    
    @Nullable
    @Override
    public String queryParam(String name) {
        return queryParams().get(name);
    }
    
    @Override
    public MultiMap formAttributes() {
        return httpServerRequest.formAttributes();
    }
    
    @Override
    public List<String> formAttributes(String attributeName) {
        return formAttributes().getAll(attributeName);
    }
    
    @Override
    public String formAttribute(String attributeName) {
        return formAttributes().get(attributeName);
    }
    
    @Override
    public HttpServerRequest getSource() {
        return httpServerRequest;
    }
}
